<template>
  <div>
    <base-header class="pb-6 pb-8 pt-5 pt-md-8 bg-gradient-success">
      <b-row>
        <!-- bootsrap自动把当前空间分为12个column，lg表示屏幕large时占几个column，md表示middle时  -->
      </b-row>
    </base-header>

    <b-container fluid class="mt--7"> </b-container>

    <b-container fluid class="mt--6">
      <b-row class="justify-content-center">
        <b-col lg="12">
          <card header-classes="bg-transparent">
            <h3 slot="header" class="mb-0">欢迎使用Real Trader</h3>
            <b-row class="icon-examples">
              <b-col lg="4" md="4">
                <!-- 
              @click的@是v-on:的简写 
              给vue组件绑定事件的时候需要加上native，给普通的html元素不需要
              -->
                <!--
             当上传空文件，v-model值为null 
             @input自带一个参数file
             -->
                <b-form-file
                  v-model="jsonfile"
                  :state="Boolean(jsonfile)"
                  placeholder="选择或拖拽json文件"
                  drop-placeholder="拖拽至此松开以上传"
                  browse-text="上传回测参数"
                  @input="load_backtesting_setting"
                  name="json"
                ></b-form-file>
                <div class="mt-3">
                  已上传文件: {{ jsonfile ? jsonfile.name : "无" }}
                </div>
              </b-col>
            </b-row>
            <hr />
            <b-row class="icon-examples">
              <b-col lg="4" md="4">
                <b-form-file
                  v-model="xlsxfile"
                  :state="Boolean(xlsxfile)"
                  placeholder="选择或拖拽xlsx文件"
                  drop-placeholder="拖拽至此松开以上传"
                  browse-text="上传策略所需xlsx文件"
                  name="xlsx"
                ></b-form-file>
                <div class="mt-3">
                  已上传文件: {{ xlsxfile ? xlsxfile.name : "无" }}
                </div>
              </b-col>
            </b-row>
            <hr />
            <b-row class="icon-examples">
              <b-col lg="4" md="4">
                <b-button
                  type="button"
                  title=""
                  @click.prevent="reload_strategy_class"
                  class="btn btn-info"
                >
                  <div>
                    <span>刷新策略</span>
                  </div>
                </b-button>
              </b-col>
              <b-col lg="4" md="4">
                <b-button
                  type="button"
                  title=""
                  @click.prevent="modify_strategy_hyperparams"
                  class="btn btn-info"
                >
                  <div>
                    <span>开始回测</span>
                  </div>
                </b-button>
              </b-col>
              <b-col lg="4" md="4">
                <b-button
                  type="button"
                  title=""
                  class="btn btn-info"
                  @click.prevent="clear_logs"
                >
                  <div>
                    <span>清除日志</span>
                  </div>
                </b-button>
              </b-col>
            </b-row>
            <hr />
            <b-row class="icon-examples">
              <b-col lg="4" md="4">
                <b-button
                  type="button"
                  title=""
                  variant="primary"
                  @click.prevent="save_charts"
                  id="id_save_charts"
                >
                  <div>
                    <span>保存图片</span>
                  </div>
                </b-button>
              </b-col>
              <b-col lg="4" md="4">
                <b-button
                  type="button"
                  title=""
                  variant="primary"
                  @click.prevent="save_metrics"
                  id="id_save_metrics"
                >
                  <div>
                    <span>保存指标</span>
                  </div>
                </b-button>
              </b-col>
              <b-col lg="4" md="4">
                <b-button
                  variant="primary"
                  type="button"
                  title=""
                  @click.prevent="save_logs"
                >
                  <div>
                    <span>保存日志</span>
                  </div>
                </b-button>
              </b-col>
            </b-row>
          </card>
        </b-col>
      </b-row>

      <b-row>
        <b-col xl="4" lg="4" class="order-xl-2">
          <b-row>
            <b-col>
              <log-card>
                <template slot="log">
                  <p v-for="line in log_content" :key="line">{{ line }}</p>
                </template>
              </log-card>
            </b-col>
          </b-row>
        </b-col>

        <b-col xl="8" lg="8" class="order-xl-1">
          <b-row>
            <b-col>
              <hyper-params-form>
                <template slot="vt_symbols">
                  <base-input
                    type="text"
                    label="合约代码(用,分隔)"
                    placeholder="symbol.DCE"
                    v-model="setting.vt_symbols"
                  >
                  </base-input>
                </template>
                <template slot="class_name">
                  <label :class="labelClasses"> 交易策略 </label>
                  <b-form-select
                    v-model="setting.class_name"
                    :options="options.class_names"
                  ></b-form-select>
                </template>

                <template slot="start">
                  <base-input
                    type="text"
                    label="开始日期(格式为yyyy-mm-dd)"
                    v-model="setting.start"
                  >
                  </base-input>
                </template>
                <template slot="end">
                  <base-input
                    type="text"
                    label="结束日期(格式为yyyy-mm-dd)"
                    v-model="setting.end"
                  >
                  </base-input>
                </template>
                <template slot="capital">
                  <base-input
                    type="text"
                    label="回测资金"
                    v-model="setting.capital"
                  >
                  </base-input>
                </template>
                <template slot="risk_free">
                  <base-input
                    type="text"
                    label="无风险利率"
                    v-model="setting.risk_free"
                  >
                  </base-input>
                </template>
                <template slot="rates">
                  <base-input
                    type="text"
                    label="手续费率(用,分隔)"
                    placeholder="2.5e-05"
                    v-model="setting.rates"
                  >
                  </base-input>
                </template>
                <template slot="slippages">
                  <base-input
                    type="text"
                    label="滑点(用,分隔)"
                    v-model="setting.slippages"
                  >
                  </base-input>
                </template>
                <template slot="sizes">
                  <base-input
                    type="text"
                    label="每手规模(用,分隔)"
                    v-model="setting.sizes"
                  >
                  </base-input>
                </template>
                <template slot="priceticks">
                  <base-input
                    type="text"
                    label="价格变动单位(用,分隔)"
                    v-model="setting.priceticks"
                  >
                  </base-input>
                </template>
              </hyper-params-form>
            </b-col>
          </b-row>
          <b-row>
            <b-col class="order-xl-1">
              <metrics-table ref="metric_table"></metrics-table>
            </b-col>
          </b-row>
          <b-row>
            <b-col xl="12">
              <b-card no-body class="border-0">
                <b-row align-v="center" slot="header">
                  <b-col cols="8">
                    <h3 class="mb-0">回测图片</h3>
                  </b-col>
                </b-row>
                <div
                  id="id_charts"
                  class="map-canvas"
                  style="height: 600px"
                ></div>
              </b-card>
            </b-col>
          </b-row>
        </b-col>
      </b-row>
      <b-modal ref="modal_strategyHps" title="设置策略超参数" hide-footer>
        <!-- style="white-space: pre-wrap;"可以显示\n为换行 -->
        <div style="white-space: pre-wrap">
          <p>策略描述：{{ strategy_docstring }}</p>
        </div>
        <!-- 注意{}被视作了空对象，判断空的js对象需要用JSON.stringify()=='{}' -->
        <div v-if="JSON.stringify(strategy_hyperparams) == '{}'">
          <p>本策略无超参数</p>
        </div>
        <b-form @submit="start_backtesting">
          <b-form-group
            v-for="(value_hps, name) in strategy_hyperparams"
            :key="name"
            :label="name"
            :label-for="'form-' + name"
          >
            <b-form-input
              :id="'form-' + name"
              type="text"
              v-model="strategy_hyperparams[name]"
            >
            </b-form-input>
          </b-form-group>
          <b-button-group>
            <b-button type="submit" variant="primary">开始回测</b-button>
          </b-button-group>
        </b-form>
      </b-modal>
    </b-container>
  </div>
</template>
<script>
import HyperParamsForm from "./portfolioBacktestingComponents/HyperParamsForm.vue";
import LogCard from "./portfolioBacktestingComponents/LogCard.vue";
import axios from "axios";
import MetricsTable from "./portfolioBacktestingComponents/MetricsTable.vue";
import port_path from "../setting_RT";

const port_path_backtest=port_path+'/backtest';

var setting_showed;
var charts_svg;

export default {
  components: {
    HyperParamsForm,
    LogCard,
    MetricsTable,
  },
  props: {
    labelClasses: {
      type: String,
      description: "Input label css classes",
      default: "form-control-label",
    },
  },
  data() {
    return {
      // setting是要显示给用户的回测设置
      setting: {
        vt_symbols: "symbol.DCE",
        start: "2020-01-15",
        end: "2021-12-15",
        capital: "1000000",
        risk_free: "0",
        class_name: "",
        rates: "2.5e-05",
        slippages: "0.2",
        sizes: "300",
        priceticks: "0.2",
      },
      log_content: [],
      options: {
        class_names: [],
      },
      modalShow_strategyHps: false,
      strategy_hyperparams: {},
      strategy_docstring: "",
      jsonfile: null,
      xlsxfile: null,
    };
  },

  methods: {
    load_backtesting_setting(jsonfile) {
      let reader = new FileReader();
      reader.readAsText(jsonfile);
      // reader在read成功后会触发onload
      reader.onload = () => {
        const json_str = reader.result;

        const path = port_path_backtest + "/load_backtesting_setting";
        //post带参数请求server创建资源，put带参数请求server修改资源，get请求server返回数据
        axios
          // JSON.parse()参数为字符串返回json对象
          // JSON.stringify()参数为json对象返回字符串
          .post(path, JSON.parse(json_str))
          .then((res) => {
            // let局部变量，var全局变量,const局部常量
            let prop;
            setting_showed = res.data;
            for (prop in setting_showed) {
              // prop是字符串所以只能res.data[prop]不能res.data.prop
              // setting修改完立即生效，但是然后再看就空了
              this.setting[prop] = setting_showed[prop];
            }
            this.refresh_log();
          })
          .catch((error) => {
            this.handle_error(error);
            alert("json文件加载失败");
          });
      };
    },
    show_class_names(class_names) {
      //在“交易策略”中显示class_names
      //对Array直接用for in遍历的是索引
      //Array.forEach(func)遍历的是对象
      this.options.class_names = [];
      class_names.forEach((class_name) => {
        this.options.class_names.push({
          value: class_name,
          text: class_name,
        });
      });
    },
    reload_strategy_class() {
      const path = port_path_backtest + "/reload_strategy_class";
      axios
        .get(path)
        .then((res) => {
          let class_names = res.data;
          this.show_class_names(class_names);
          this.$notify({
            type: "info",
            message: "策略已刷新",
          });

          this.refresh_log();
        })
        .catch((error) => {
          this.handle_error(error);
        });
    },
    activate_button_by_id(id) {
      let button = document.getElementById(id);
      button.disabled = false;
      button.active = true;
    },
    disable_button_by_id(id) {
      let button = document.getElementById(id);
      button.disabled = true;
    },
    modify_strategy_hyperparams() {
      if (this.xlsxfile) {
        this.send_xlsxfile(this.xlsxfile);
      } else {
        this.delete_file("xlsx");
      }

      let prop;
      setting_showed = {};
      for (prop in this.setting) {
        setting_showed[prop] = this.setting[prop];
      }
      this.strategy_hyperparams = {};

      const path = port_path_backtest + "/modify_strategy_hyperparams";
      axios
        .post(path, setting_showed)
        .then((res) => {
          if (res.data != "False") {
            let name;
            for (name in res.data) {
              if (name == "docstring") {
                this.strategy_docstring = res.data[name];
              } else {
                this.strategy_hyperparams[name] = res.data[name];
              }
            }
            this.refresh_log();
            this.$refs["modal_strategyHps"].show();
          } else {
            this.refresh_log();
          }
        })
        .catch((error) => {
          this.handle_error(error);
        });
    },
    start_backtesting(evt) {
      evt.preventDefault();
      this.disable_button_by_id("id_save_charts");
      this.disable_button_by_id("id_save_metrics");

      const path = port_path_backtest + "/start_backtesting";
      axios
        .post(path, this.strategy_hyperparams)
        .then((res) => {
          if (res.data != "False") {
            this.show_metrics(res.data["result_metrics"]);
            this.show_charts();
            this.activate_button_by_id("id_save_charts");
            this.activate_button_by_id("id_save_metrics");
            this.refresh_log();
            this.$notify({
              type: "info",
              message: "回测成功！",
            });
          } else {
            this.$notify({
              type: "warning",
              message: "回测失败！",
            });

            this.refresh_log();
          }
        })
        .catch((error) => {
          this.handle_error(error);
        });

      this.$refs["modal_strategyHps"].hide();
      //$notify()会在<notifications>处显示通知
      this.$notify({
        type: "info",
        message: "启动回测中...",
      });
    },
    clear_logs() {
      const path = port_path_backtest + "/log";
      axios
        .delete(path)
        .then((res) => {
          this.refresh_log();
        })
        .catch((error) => {
          this.handle_error(error);
        });
    },
    refresh_log() {
      const path = port_path_backtest + "/log";
      axios
        .get(path)
        .then((res) => {
          this.log_content = res.data;
        })
        .catch((error) => {
          this.handle_error(error);
        });
    },
    handle_error(error) {
      this.$notify({
        type: "warning",
        message: error,
      });
    },
    show_metrics(metrics) {
      this.$refs.metric_table.update_tableData(metrics);
      this.$refs.metric_table.bool_show_table = true;
    },
    show_charts() {
      const path = port_path_backtest + "/charts";
      axios
        .get(path)
        .then((res) => {
          if (res.data != "False") {
            // this.charts_png = res.data["png_b64"];
            // const ele_png = document.getElementById("id_png");
            // ele_png.src+=this.charts_png;

            // 这里res.data['svg']是字符串类型的<svg>标签,故用DOMParser转为html对象。
            let parser = new DOMParser();
            charts_svg = parser.parseFromString(
              res.data["svg"],
              "text/html"
            ).body;
            let father_of_charts;
            father_of_charts = document.getElementById("id_charts");
            if (father_of_charts.hasChildNodes()) {
              father_of_charts.replaceChild(
                charts_svg,
                father_of_charts.childNodes[0]
              );
            } else {
              father_of_charts.appendChild(charts_svg);
            }
          } else {
            this.$notify({
              type: "warning",
              message: "回测结果为空！",
            });
            this.refresh_log();
          }
        })
        .catch((error) => {
          this.handle_error(error);
        });
    },
    load_class_names() {
      const path = port_path_backtest + "/class_names";
      axios
        .get(path)
        .then((res) => {
          let class_names = res.data;
          this.show_class_names(class_names);
          this.refresh_log();
        })
        .catch((error) => {
          this.handle_error(error);
        });
    },
    save_charts() {
      const path = port_path_backtest + "/get_file_png";
      axios
        .get(path, { responseType: "blob" })
        .then((res) => {
          let blob = new Blob([res.data]);
          let url = window.URL.createObjectURL(blob);
          let temp = document.createElement("a");
          temp.href = url;
          temp.download = "回测图表.png";
          temp.target = "_blank";
          temp.click();
          window.URL.revokeObjectURL(url);
          this.delete_file("png");

          this.$notify({
            type: "info",
            message: "文件下载中...",
          });
        })
        .catch((error) => {
          this.handle_error(error);
        });
    },
    save_metrics() {
      const path = port_path_backtest + "/get_file_metrics";
      axios
        .get(path, { responseType: "blob" })
        .then((res) => {
          let blob = new Blob([res.data]);
          let url = window.URL.createObjectURL(blob);
          let temp = document.createElement("a");
          temp.href = url;
          temp.download = "回测指标.csv";
          temp.target = "_blank";
          temp.click();
          window.URL.revokeObjectURL(url);
          this.delete_file("metrics");

          this.$notify({
            type: "info",
            message: "文件下载中...",
          });
        })
        .catch((error) => {
          this.handle_error(error);
        });
    },
    save_logs() {
      const path = port_path_backtest + "/get_file_log";
      axios
        .get(path, { responseType: "blob" })
        .then((res) => {
          let blob = new Blob([res.data]);
          let url = window.URL.createObjectURL(blob);
          let temp = document.createElement("a");
          temp.href = url;
          temp.download = "回测日志.txt";
          temp.target = "_blank";
          temp.click();
          window.URL.revokeObjectURL(url);
          this.delete_file("log");

          this.$notify({
            type: "info",
            message: "文件下载中...",
          });
        })
        .catch((error) => {
          this.handle_error(error);
        });
    },
    delete_file(filename) {
      const path = port_path_backtest + "/delete_file_" + filename;
      axios
        .delete(path)
        .then((res) => {})
        .catch((error) => {
          this.handle_error(error);
        });
    },
    send_xlsxfile(xlsxfile) {
      const path = port_path_backtest + "/parse_xlsxfile";
      const formData = new FormData();
      formData.append("xlsx", xlsxfile);
      axios
        .post(path, formData, { "Content-type": "multipart/form-data" })
        .then((res) => {})
        .catch((error) => {
          this.handle_error(error);
        });
    },
  },
  created() {
    this.refresh_log();
    this.load_class_names();
    // window.myData = this;
  },
  mounted() {
    this.disable_button_by_id("id_save_charts");
    this.disable_button_by_id("id_save_metrics");
  },
};
</script>
<style>
</style>
